package sdc.media.motorbike.Controller;

import java.util.ArrayList;
import java.util.Random;

import sdc.media.motorbike.Model.Car;
import sdc.media.motorbike.Model.Motor;
import sdc.media.motorbike.Model.UserData;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Pool;

public class WorldGame {

	// World state
	public static enum STATE {
		WORLD_RUNNING,
		WORLD_NEXT_LEVEL,
		WORLD_GAME_OVER
	}
	
	public STATE state;
	
	public UserData worldboxUserdata, streetUserdata, motorbikeUserdata;
	
	// Objects, etc...
	public float timePlay = 0f;
	public float timeOver = 0f;
	public float x_tree = 0;
	public float y_tree = 0;
	public float x_den_ham = 0;
	public float y_den_ham = 0;
	public float x_den_duong = 0;
	public float y_den_duong = 0;
	public int timeRemaining;
	public int score = 0;
	public int map = 1;
	public int carType;
	public int slot;
	public boolean slot1, slot2, slot3;
	public float speed;
	public float timeSpeed = 0f;
	public float distance;
	public float car_1_distance;
	public float car_2_distance;
	public float car_3_distance;
	public float WIDTH, HEIGHT;
	public float sizeX = Gdx.graphics.getWidth() / 480.0f;
	public float sizeY = Gdx.graphics.getHeight() / 800.0f;
	
	// World Box
	public World worldBox2D = new World(new Vector2(0, 0), true);
	public Box2DDebugRenderer debugRenderer = new Box2DDebugRenderer();

	public BodyDef myBodyDef = new BodyDef();
	public FixtureDef myFixtureDef = new FixtureDef();
	public PolygonShape polygonShape = new PolygonShape();
	
	public Body world_box;
	public Body street_left, street_right;
	
	public Array<Body> bodies = new Array<Body>();
	
	public MyContactListener contactListener = new MyContactListener();
	
	public final Motor motorbike;
	public final Pool<Motor> motorPool = new Pool<Motor>() {

		@Override
		protected Motor newObject() {
			// TODO Auto-generated method stub
			return new Motor();
		}
		
	};
	
	public final ArrayList<Car> tempCars = new ArrayList<Car>();
	public final ArrayList<Car> activeCars = new ArrayList<Car>();
	public final Pool<Car> carPool = new Pool<Car>() {

		@Override
		protected Car newObject() {
			// TODO Auto-generated method stub
			return new Car();
		}
		
	};
	
	public WorldGame() {
		// TODO Auto-generated constructor stub
		speed = Setting.speed;
		distance = 0;
		carType = Setting.carType;
		car_1_distance = 500;
		car_2_distance = 1000;
		car_3_distance = 250;
		
		// create objects, etc...
		//worldBox2D = new World(new Vector2(0f, 0f), true);
		//debugRenderer = new Box2DDebugRenderer();
		//contactListener = new MyContactListener();
		worldBox2D.setContactListener(contactListener);
		WIDTH = Gdx.graphics.getWidth() / Constant.PIXEL_PER_METER;
		HEIGHT = Gdx.graphics.getHeight() / Constant.PIXEL_PER_METER;
		
		/*polygonShape = new PolygonShape();
		myBodyDef = new BodyDef();
		myFixtureDef = new FixtureDef();*/
		myFixtureDef.shape = polygonShape;
		myFixtureDef.density = 1;
		//myFixtureDef.friction = 0.3f;
		//myFixtureDef.restitution = 0.5f;
		
		// World is a box of objects
		
		// Static Body
		// World Box
		myBodyDef.type = BodyType.StaticBody;
		myBodyDef.position.set(0, 0);
		
		world_box = worldBox2D.createBody(myBodyDef);
		polygonShape.setAsBox(WIDTH / 2, 0.025f, new Vector2(0, - HEIGHT / 2), 0);
		world_box.createFixture(myFixtureDef);
		polygonShape.setAsBox(WIDTH / 2, 0.025f, new Vector2(0, HEIGHT / 2), 0);
		//world_box.createFixture(myFixtureDef);
		polygonShape.setAsBox(0.025f, HEIGHT / 2, new Vector2(- WIDTH / 2, 0), 0);
		world_box.createFixture(myFixtureDef);
		polygonShape.setAsBox(0.025f, HEIGHT / 2, new Vector2(WIDTH / 2, 0), 0);
		world_box.createFixture(myFixtureDef);
		
		// Street
		double alpha = Math.atan(WIDTH / 3 / HEIGHT);
		float w = (float) (HEIGHT / 2 / Math.cos(alpha));
		myBodyDef.type = BodyType.StaticBody;
		myBodyDef.position.set(- WIDTH / 2.5f, HEIGHT / 2);
		street_left =  worldBox2D.createBody(myBodyDef);
		polygonShape.setAsBox(0.05f, w, new Vector2(0, - HEIGHT / 2), (float) (-alpha));
		street_left.createFixture(myFixtureDef);
		
		myBodyDef.position.set(WIDTH / 2.5f, HEIGHT / 2);
		street_right =  worldBox2D.createBody(myBodyDef);
		polygonShape.setAsBox(0.05f, w, new Vector2(0, - HEIGHT / 2), (float) alpha);
		street_right.createFixture(myFixtureDef);
		
		// MotorBike
		motorbike = motorPool.obtain();
		motorbike.init(worldBox2D, "Motor_Bike", new Vector2(0, - HEIGHT / 2 + 15 * sizeY), carType);
		
		// Car
		//createCar(new Vector2(- WIDTH / 10, HEIGHT / 2 - 1), 1);
		createCar(new Vector2(0, HEIGHT / 2 * 1.25f), 0);
		//createCar(new Vector2(WIDTH / 10, HEIGHT / 2 - 1), 2);
		
		street_left.setUserData(new UserData("Street_Left", null));
		street_right.setUserData(new UserData("Street_Right", null));
		world_box.setUserData(new UserData("World_Box", null));
		
		//bodies = new Array<Body>();
		polygonShape.dispose();
		
		this.state = STATE.WORLD_RUNNING;
	}
	
	public void update(float deltaTime) {
		// Update every object in the world
		refreshWorld(deltaTime);
		
		// Check condition
		checkLevelEnd();
		checkGameOver();
	}
	
	private void refreshWorld(float deltaTime) {
		// TODO Auto-generated method stub
		
		distance += speed * 200 * deltaTime;
		car_1_distance += 200 * deltaTime;
		car_2_distance += 200 * deltaTime;
		car_3_distance += 200 * deltaTime;
		//System.out.println(distance);
		// update objects, ...
		motorbike.update(this);
		
		// Car
		if((int) distance >= Constant.STREET_1_LENGTH) {
			map = 2;
		}
		if((int) distance >= Constant.STREET_1_LENGTH + Constant.STREET_2_LENGTH) {
			map = 3;
		}
		if((int) distance >= Constant.STREET_1_LENGTH + Constant.STREET_2_LENGTH + Constant.STREET_3_LENGTH) {
			map = 1;
		}
		
		if((int) car_1_distance >= 1000 - (map - 1) * 100) {
			if(slot == 3) {
				createCar(new Vector2(- WIDTH / 10, HEIGHT / 2 * 1.25f), 2);
				slot = 1;
				slot1 = true;
				slot3 = false;
			}
			else {
				createCar(new Vector2(WIDTH / 10, HEIGHT / 2 * 1.25f), 3);
				slot = 3;
				slot3 = true;
				slot1 = false;
			}
			car_1_distance = 0;
		}
		
		if((int) car_2_distance >= 2000 - (map - 1) * 100) {
			int rand = new Random().nextInt(2);
			if(rand == 1)
				createCar(new Vector2(0, HEIGHT / 2 * 1.25f), 0);
			else
				createCar(new Vector2(0, HEIGHT / 2 * 1.25f), 1);
			slot2 = true;
			car_2_distance = 0;
		}
		if((int) car_2_distance >= 200) {
			slot2 = false;
		}
		
		
		if((int) car_3_distance >= 500 - (map - 1) * 100) {
			int rand = new Random().nextInt(2);
			if(slot == 1) {
				if(rand == 1 && !slot2)
					createCar(new Vector2(WIDTH / 10, HEIGHT / 2 * 1.25f), 4);
				else
					if(!slot2)
						createCar(new Vector2(0, HEIGHT / 2 * 1.25f), 4);
			} else if(slot == 3 && !slot2) {
				if(rand == 1)
					createCar(new Vector2(- WIDTH / 10, HEIGHT / 2 * 1.25f), 4);
				else
					if(!slot2)
						createCar(new Vector2(0, HEIGHT / 2 * 1.25f), 4);
			}
			/*if(rand == 1)
				createCar(new Vector2(- WIDTH / 10, HEIGHT / 2 - 1), 3);
			else if(rand == 2)
				createCar(new Vector2(0, HEIGHT / 2 - 1), 3);
			else
				createCar(new Vector2(WIDTH / 10, HEIGHT / 2 - 1), 3);*/
			car_3_distance = 0;
		}
		
		// Car Array
		for (Car car : this.activeCars) {
			tempCars.add(car);
		}
		activeCars.clear();
		for (Car car : tempCars) {
			if(car.getState() == Car.STATE.EXIST)
				this.activeCars.add(car);
		}
		tempCars.clear();
		
		Car car;
		int len = activeCars.size();
		if(len > 0) {
			for (int i = 0; i < len; i++) {
				car = activeCars.get(i);
				car.update(this, deltaTime);
				if(car.getState() == Car.STATE.DIE) {
					//activeCars.remove(i);
					//carPool.free(car);
					worldBox2D.destroyBody(car.getBody());
					score+= (int)speed;
				}
			}
		}
		
		x_tree += 2 * speed * sizeX;
		y_tree += 10 * speed * sizeY;
		if(y_tree >= Gdx.graphics.getHeight()) {
			x_tree = 0;
			y_tree = 0;
		}
		
		x_den_ham += 3 * speed * sizeX;
		y_den_ham += 10 * speed * sizeY;
		if(y_den_ham >= Gdx.graphics.getHeight() / 2) {
			x_den_ham = 0;
			y_den_ham = 0;
		}
		
		x_den_duong += 3 * speed * sizeX;
		y_den_duong += 10 * speed * sizeY;
		if(y_den_duong >= Gdx.graphics.getHeight() / 2) {
			x_den_duong = 0;
			y_den_duong = 0;
		}
		
		// Update all bodies
		worldBox2D.getBodies(bodies);
	}

	private void createCar(Vector2 position, int type) {
		// TODO Auto-generated method stub
		Car car = carPool.obtain();
		car.init(worldBox2D, "Car", position, type);
		activeCars.add(car);
		//System.out.println(activeCars.size());
	}
	
	private void checkLevelEnd() {
		// TODO Auto-generated method stub
		
		// state = STATE.WORLD_NEXT_LEVEL;
	}

	private void checkGameOver() {
		// TODO Auto-generated method stub
		if(contactListener.isGameOver == true) {
			state = STATE.WORLD_GAME_OVER;
			Assets.playSound(Assets.vacham);
			for (Car car : activeCars) {
				car.getBody().setLinearVelocity(0, 0);
			}
			dispose();
		}
	}
	
	public void dispose() {
		// dispose objects in the world
		worldboxUserdata = null;
		streetUserdata = null;
		motorbikeUserdata = null;
		//worldBox2D.destroyBody(world_box);
		//worldBox2D.destroyBody(street_left);
		//worldBox2D.destroyBody(street_right);
		
		myBodyDef = null;
		myFixtureDef = null;
		contactListener = null;
		
		motorPool.free(motorbike);
		motorPool.clear();
		carPool.clear();
		activeCars.clear();
		
		bodies.clear();
	}
}
